package river_city_ransom.services;

import java.util.List;

import river_city_ransom.types.Commande;
import exceptions.ComposantError;

public interface GestionCombat {
	/** _________________________________________________________ OBSERVATORS */
	public Terrain terrain();
	public PersonnageJouable alex();
	public PersonnageJouable ryan();
	public Gangster slick();
	public List<Gangster> gangsters();
	public boolean estPret();
	
	// pre collision(GC, equipable1, equipable2) require equipable1 != NULL ^ equipable2 != NULL
	public boolean collision(Equipable equipable1, Equipable equipable2) throws ComposantError;
	
	// pre collisionObjetLance(GC, personnage, objets) require personnage != NULL ^ objets != NULL
	public Objet collisionObjetLance(Personnage personnage, List<Objet> objets) throws ComposantError;
	
	// pre collisionObjetRamasse(GC, personnage, lo) require personnage != NULL ^ lo != NULL
	public Objet collisionObjetRamasse(Personnage personnage, List<Objet> objets) throws ComposantError;
	
	// pre estFrappe(GC, personnage) require personnage != NULL
	public Personnage estFrappe(Personnage personnage) throws ComposantError;
	
	// pre estPorte(GC, personnage) require personnage != NULL
	public Personnage estPorte(Personnage personnage) throws ComposantError;
	
	public Commande commandesEnnemis();
	public Commande commandeAlex();
	public Commande commandeRyan();
	
	/** ________________________________________________________ CONSTRUCTORS */
	
	// pre init(t) require t != NULL
	// post terrain(init(t)) = t
	// post commandeAlex(init(t)) = NULL
	// post commandeRyan(init(t)) = NULL
	// post alex(init(t)) = PersonnageJouable::init("Alex", Dimensions::init(10, 50, 10), 15, 100)
	// post ryan(init(t)) = PersonnageJouable::init("Ryan", Dimensions::init(10, 50, 10), 15, 100)
	// post slick(init(t)) = Gangster::init("Slick", Dimensions::init(10, 50, 10), 15, 100)
	// post gangsters(init(t)) = {Gangster::init("GenericDudes", Dimensions::init(10, 50, 10), 15, 100),
	//  						Gangster::init("HomeBoys", Dimensions::init(10, 50, 10), 15, 100),
	//  						Gangster::init("Squids", Dimensions::init(10, 50, 10), 15, 100),
	//  						Gangster::init("Squids2", Dimensions::init(10, 50, 10), 15, 100)}
	// post estPret(init(t)) = false
	// post commandesEnnemis(init(t)) = COMMANDE.RIEN
	// post estFrappe(init(t),p) = NULL pour tout p appartenant à Personnage
	// estPorte(init(t),p) = NULL pour tout p appartenant à Personnage
	public void init(Terrain t) throws ComposantError;
	
	/** __________________________________________________________ INVARIANTS */
	/*
	 * 	equipable1 appartient à {alex(), ryan(), slick(),  gangsters(), Terrain:objets(terrain(GC))},
		equipable2 appartient à {alex(), ryan(), slick(),  gangsters(), Terrain:objets(terrain(GC))} \\ equipable2
		collision(GC, equipable1, equipable2) = true si (Dimensions::largeur(Equipable::dimensions(equipable1)) + Dimensions::largeur(Equipable::dimensions(equipable2))\2 
														<= Position::x(Equipable::position(equipable1)) - Position::x(Equipable::position(equipable2))) ||
														(Dimensions::profondeur(Equipable::dimensions(equipable1)) + Dimensions::profondeur(Equipable::dimensions(equipable2))\2
														<= Position::y(Equipable::position(equipable1)) - Position::y(Equipable::position(equipable2))) ||
														(Dimensions::hauteur(Equipable::dimensions(equipable1)) + Dimensions::hauteur(Equipable::dimensions(equipable2))\2
														<= Position::z(Equipable::position(equipable1)) - Position::z(Equipable::position(equipable2))) ||
											  = false sinon
	*/
	/*
	 * personnage appartient à {alex(), ryan(), slick(),  gangsters()}
		collisionObjetLance(GC, personnage, objets) = O s'il existe un O appartenant à objets tel que collision(personnage, O) = true ^ Objet::estJete(O) != 0
											        = NULL s'il n'existe pas un O appartenant à objets tel que collision(personnage, O) = false ^ Objet::estJete(O) != 0
	*/	
	
	/*	collisionObjetRamasse(GC, personnage, objets) = O s'il existe un O appartenant à objets tel que collision(personnage, O) = true ^ Objet::estJete(O) = 0
											          = NULL s'il n'existe pas un O appartenant à objets tel que collision(personnage, O) = false ^ Objet::estJete(O) = 0
	 */
	
	
	/** ___________________________________________________________ OPERATORS */
	// pre gerer(GC, commande1, commande2) require estPret(GC)
	// commandeAlex(gerer(GC, commande1,commande2)) = commande1
	// commandeRyan(gerer(GC, commande1,commande2)) = commande2
	// post estPret(gerer(GC, commande1,commande2)) = estPret(GC)
	// post alex(gerer(GC, commande1,commande2)) = alex(gererCommande(GC,alex(GC),commande1))
	// post ryan(gerer(GC, commande1,commande2)) = ryan(gererCommande(GC,ryan(GC),commande2))
	// post slick(gerer(GC, commande1,commande2)) = slick(gererCommande(GC,slick(GC), commandesEnnemis(GC)))
	// post gangsters(gerer(GC, commande1, commande2)) = pour tout g appartenant a gangsters
	//                                                  gererCommande(GC, g, commandesEnnemis(GC))
	// post  Terrain::objets(terrain(gerer(GC, commande1,commande2))) = impossible a spécifier aisement
	public void gerer(Commande commande1, Commande commande2) throws ComposantError;
	
	// pre avantPasJeu(GC) require !estPret(GC)
	// post commandeAlex(init(t)) = commandeAlex(GC)
	// post commandeRyan(init(t)) = commandeRyan(GC)
	/* post terrain(avantPasJeu(GC))  = terrain(GC) si Bloc::type(Terrain::bloc(terrain(GC),Personnage::position(alex(GC)))) != BLOC_TYPE.FOSSE
												^ Bloc::type(Terrain::bloc(terrain(GC),Personnage::position(ryan(GC)))) != BLOC_TYPE.FOSSE
									
									= Terrain::setBloc(Terrain::delBloc(terrain(GC), (Terrain::bloc(terrain(GC),Personnage::position(alex(GC))))), Bloc::init(BLOC_TYPE.VIDE, PersonnageJouable::position(alex(GC))))
									 si Bloc::type(Terrain::bloc(terrain(GC),Personnage::position(alex(GC)))) = BLOC_TYPE.FOSSE
									^ Bloc::type(Terrain::bloc(terrain(GC),Personnage::position(ryan(GC)))) != BLOC_TYPE.FOSSE
									
									= Terrain::setBloc(Terrain::delBloc(terrain(GC), (Terrain::bloc(terrain(GC),Personnage::position(ryan(GC))))), Bloc::init(BLOC_TYPE.VIDE, PersonnageJouable::position(ryan(GC))))
									 si Bloc::type(Terrain::bloc(terrain(GC),Personnage::position(alex(GC)))) != BLOC_TYPE.FOSSE
									^ Bloc::type(Terrain::bloc(terrain(GC),Personnage::position(ryan(GC)))) = BLOC_TYPE.FOSSE
									
									= Terrain::setBloc(Terrain::delBloc(Terrain::setBloc(Terrain::delBloc(terrain(GC), 
									(Terrain::bloc(terrain(GC),Personnage::position(alex(GC))))), Bloc::init(BLOC_TYPE.VIDE, PersonnageJouable::position(alex(GC))))
									, (Terrain::bloc(terrain(GC),Personnage::position(ryan(GC))))), Bloc::init(BLOC_TYPE.VIDE, PersonnageJouable::position(ryan(GC)))), 
									 si Bloc::type(Terrain::bloc(terrain(GC),Personnage::position(alex(GC))) = BLOC_TYPE.FOSSE
									^ Bloc::type(Terrain::bloc(terrain(GC),Personnage::position(ryan(GC))) = BLOC_TYPE.FOSSE
									
									
									
	*/
	// post alex(avantPasJeu(GC)) = alex(GC)
	// post ryan(avantPasJeu(GC)) = ryan(GC)
	// post slick(avantPasJeu(GC)) = slick(GC)
	// post gangsters(avantPasJeu(GC)) = gangsters(GC)
	// post estPret(avantPasJeu(GC)) = true
	// post commandesEnnemis(avantPasJeu(GC)) = commandesEnnemis(GC)
	// post estPorte(avantPasJeu(GC),p) = NULL pour tout p appartenant à Personnage
	// post estFrappe(avantPasJeu(GC),p) = NULL pour tout p appartenant à Personnage
	// post commandesEnnemis(gerer(GC, commande1,commande2)) = c appartenant à COMMANDE
	public void avantPasJeu() throws ComposantError;
	
	// voir spec
	public void gererCommande(Personnage personnage, Commande commande) throws ComposantError;
	
}
